/**
 *  @file  vsTimeRollingOutput.h version 1.0 2007-11-20 
 *  @brief Define class TimeRollingOutput.
 *  Copyright (c) 2007 by @author <abitfree@gmail.com>
 */

#ifndef __VS_TIME_ROLLING_OUTPUT_H__
#define __VS_TIME_ROLLING_OUTPUT_H__

#include <ctime>
#include <cassert>
#include "vsFileOutput.h"

namespace vs {
namespace log {



/// The log output class, rolling by time. 
/*!
  The abstraction of the logging object, it can be printed to screen, 
  or to a file, and so on.
*/

/**
 * @brief The time rolling log output class.
 *
 * The log output object will put log messages in splitted files rolling by time.
 */
class TimeRollingOutput : public FileOutput
{

public:

	/// The log output type.
	enum E_LOG_OUTPUT_TYPE
	{
		ROLLING_BY_YEAR,		///< The log output is rolled by time, unit is year.
		ROLLING_BY_MONTH,		///< The log output is rolled by time, unit is month.
		ROLLING_BY_DAY,			///< The log output is rolled by time, unit is day.
		ROLLING_BY_HOUR,		///< The log output is rolled by time, unit is hour.
		ROLLING_BY_MINUTE,		///< The log output is rolled by time, unit is minute.
		ROLLING_BY_SECOND		///< The log output is rolled by time, unit is second.
	};

public:
	/// The constructor.
	TimeRollingOutput(E_LOG_OUTPUT_TYPE eType = ROLLING_BY_DAY, const char *pszFileHeader = 0, const char *pszDirectory = 0);
	
	/// The destructor.
	~TimeRollingOutput();

	/// Write a log message, it is usually called by the Logger class objects.
	virtual int Write(const char *pszMsg, int nSize, struct tm *pWriteTime);

protected:
	/// Get the file name of the current FileOutput.
	virtual const char* GetFileName();
    
	/// Switch a file, if needed.
	virtual void SwitchFile(int nWriteSize, struct tm *pWriteTime);
	
private:
	// The log output's open time.
	struct tm m_TmNow;

	/// The log output type.
	E_LOG_OUTPUT_TYPE m_eLogOutputType;
    
};

#define INLINE inline

INLINE
TimeRollingOutput::TimeRollingOutput(E_LOG_OUTPUT_TYPE eType, const char *pszFileHeader, const char *pszDirectory)
	: FileOutput(pszFileHeader, pszDirectory), m_eLogOutputType(eType)
{
	time_t tNow = time(0);
	struct tm *pNow = localtime(&tNow);
	memcpy(&m_TmNow, pNow, sizeof(m_TmNow));
}

INLINE
TimeRollingOutput::~TimeRollingOutput()
{
    Close();
}

INLINE
int TimeRollingOutput::Write(const char *pszMsg, int nSize, struct tm *pWriteTime)
{
	SwitchFile(nSize, pWriteTime);
	return fwrite(pszMsg, 1, nSize, m_File);
}

INLINE
const char* TimeRollingOutput::GetFileName()
{
	static char file_name[MAX_FILE_NAME];
	_snprintf(file_name, MAX_FILE_NAME, "%s\\%s_%04d%02d%02d_%02d%02d%02d.log", GetLogDirectory(), GetLogFileNameHeader(),
		m_TmNow.tm_year+1900, m_TmNow.tm_mon+1, m_TmNow.tm_mday,
		m_TmNow.tm_hour, m_TmNow.tm_min, m_TmNow.tm_sec);
	return file_name;
}

INLINE
void TimeRollingOutput::SwitchFile(int nWriteSize, struct tm *pWriteTime)
{
	assert(pWriteTime != 0);

	bool bSwitch = false;
	switch (m_eLogOutputType)
	{
	case ROLLING_BY_SECOND:
		if ((pWriteTime->tm_sec != m_TmNow.tm_sec) ||
            (pWriteTime->tm_min != m_TmNow.tm_min) ||
            (pWriteTime->tm_hour != m_TmNow.tm_hour) ||
            (pWriteTime->tm_mday != m_TmNow.tm_mday) || 
            (pWriteTime->tm_mon != m_TmNow.tm_mon) || 
            (pWriteTime->tm_year != m_TmNow.tm_year))
		{
			bSwitch = true;
		}
		break;
	case ROLLING_BY_MINUTE:
		if ((pWriteTime->tm_min != m_TmNow.tm_min) ||
            (pWriteTime->tm_hour != m_TmNow.tm_hour) ||
            (pWriteTime->tm_mday != m_TmNow.tm_mday) || 
            (pWriteTime->tm_mon != m_TmNow.tm_mon) || 
            (pWriteTime->tm_year != m_TmNow.tm_year))
		{
			bSwitch = true;
		}
		break;
	case ROLLING_BY_HOUR:
		if ((pWriteTime->tm_hour != m_TmNow.tm_hour) ||
            (pWriteTime->tm_mday != m_TmNow.tm_mday) || 
            (pWriteTime->tm_mon != m_TmNow.tm_mon) || 
            (pWriteTime->tm_year != m_TmNow.tm_year))
		{
			bSwitch = true;
		}
		break;
	case ROLLING_BY_DAY:
        if ((pWriteTime->tm_mday != m_TmNow.tm_mday) || 
            (pWriteTime->tm_mon != m_TmNow.tm_mon) || 
            (pWriteTime->tm_year != m_TmNow.tm_year))
		{
			bSwitch = true;
		}
		break;
	case ROLLING_BY_MONTH:
		if ((pWriteTime->tm_mon != m_TmNow.tm_mon) ||
            (pWriteTime->tm_year != m_TmNow.tm_year))
		{
			bSwitch = true;
		}
		break;
	case ROLLING_BY_YEAR:
		if (pWriteTime->tm_year != m_TmNow.tm_year)
		{
			bSwitch = true;
		}
		break;
	default:
		break;
	}

	if (bSwitch)
	{
		memcpy(&m_TmNow, pWriteTime, sizeof(m_TmNow));
        // will close log file in Open().
		Open();
	}
}

} // namespace log
} // namespace vs

#endif // __VS_TIME_ROLLING_OUTPUT_H__
